#include <QCoreApplication>
#include <QTimer>
#include <QSerialPort>
#include <QThread>
#include <QDateTime>
#include <QDebug>

#include "setting/JSON_settings.h"
#include "coordinator/coordinator.h"
#include "papercontrol/papercontrol.h"

#define CB_RESPONSE_WAIT_TIME	1			// 0.1 seconds

PaperControl_Worker::PaperControl_Worker(QObject *parent)
	: QObject(parent)
{
	m_port = nullptr;
	m_worker_timer = nullptr;

	m_pos_status = PAPER_POS_0;
}

PaperControl_Worker::~PaperControl_Worker()
{
	stop_work();
}

void PaperControl_Worker::start_work()
{
	if (!m_param.port.isNull())
		m_port = new QSerialPort(m_param.port, this);
	if (m_port) {
		m_port->setBaudRate(m_param.baud);
        m_port->setDataBits(m_param.data_bits);
		m_port->setStopBits(m_param.stop_bits);
		m_port->setParity(m_param.parity);
		if (!m_port->open(QIODevice::ReadWrite)) {
            qInfo() << "[PAPERCONTROL]Open Serial Port " << m_param.port << " Failed!";
            delete m_port;
            m_port = nullptr;
            QTimer::singleShot(10000, this, &PaperControl_Worker::start_work);
			return;
		}
	}

    qInfo() << "[PAPERCONTROL]Connecting Control Board Module at " << m_param.port;
    qInfo() << "[PAPERCONTROL]    Baud: " << m_param.baud;
    qInfo() << "[PAPERCONTROL]DataBits: " << m_param.data_bits;
    qInfo() << "[PAPERCONTROL]  Parity: " << m_param.parity;
    qInfo() << "[PAPERCONTROL]StopBits: " << m_param.stop_bits;

    m_response.clear();
    m_wait_time = 0x0100;
	m_worker_timer = new QTimer(this);
	m_pos_check_timer = new QTimer(this);

    connect(m_worker_timer, &QTimer::timeout, this, &PaperControl_Worker::process_cmd_resp);
    m_worker_timer->start(100);
    connect(m_pos_check_timer, &QTimer::timeout, this, &PaperControl_Worker::check_status);
    m_pos_check_timer->start(500);
    action(1);
}

void PaperControl_Worker::stop_work()
{
	if (m_pos_check_timer)
		delete m_pos_check_timer;
	if (m_worker_timer)
		delete m_worker_timer;
	if (m_port)
		delete m_port;
	m_pos_check_timer = nullptr;
	m_worker_timer = nullptr;
	m_port = nullptr;
}

void PaperControl_Worker::action(uint8_t act)
{
	const uint8_t cmd[] = { 0xaa, 0x05, 0x30, 0x5f, 0x00 };
	QByteArray ba_cmd((char *) cmd, sizeof(cmd));

	ba_cmd[4] = act;
	transfer_cmd(ba_cmd);

	qInfo() << "[PAPERCONTROL]Position" << act;
}

void PaperControl_Worker::setSerialParam(SerialConfig param)
{
	PaperControl_Worker::m_param = param;
}

void PaperControl_Worker::process_cmd_resp()
{
	if (m_port) {
        m_wait_time++;
        if (m_wait_time > CB_RESPONSE_WAIT_TIME && m_cmd_list.length()) {
            QByteArray cmd = m_cmd_list.first();
            m_cmd_list.removeFirst();
			m_port->write(cmd);
			m_wait_time = 0;
		}
		m_response += m_port->readAll();
        QByteArray msg_ba = rcsv_analyze(m_response);
		if (msg_ba.length()) {
            parse_msg(msg_ba);
            m_wait_time = CB_RESPONSE_WAIT_TIME - 1;	// send next command 100ms later after received the response
		}
	}
	else
		m_cmd_list.clear();
}

void PaperControl_Worker::check_status()
{
	const uint8_t cmd[] = { 0xaa, 0x03, 0x30 };
	QByteArray ba_cmd((char *) cmd, sizeof(cmd));
	transfer_cmd(ba_cmd);
}

void PaperControl_Worker::transfer_cmd(QByteArray cmd)
{
	m_cmd_list.append(cmd);
}

QByteArray PaperControl_Worker::rcsv_analyze(QByteArray &raw)
{
	QByteArray act_resp, resp_body;

    unsigned char stx[] = {
    	0xbb,							// STX(1 bytes)
    	0x00
    };

	while (raw.length() >= 2) {
		if (!raw.startsWith((char *) stx)) {
			raw.remove(0, 1);
			continue;
		}

        uint8_t resp_length;
		// 1 bytes packet size
		resp_length = raw[1];
		if (resp_length > 0x05 || resp_length < 0x04) {
			raw.remove(0, 2);
			continue;
		}
		if (resp_length > raw.length())
			break;
		// Response packet
		act_resp = raw.mid(0, resp_length);
		raw.remove(0, resp_length);
		// Reponse body
		resp_body = act_resp.mid(2, act_resp.length() - 2);
		return resp_body;
	}

    return QByteArray();
}

void PaperControl_Worker::parse_msg(QByteArray msg)
{
	uint8_t msg_code, msg_data;

	msg_code = msg[0];
	msg_data = msg[1];
    if (msg_code & 0xbf)
		m_pos_status = msg_data;;
}

PaperController::PaperController(QObject *parent)
{
    QSettings settings(JSONSettings::GetSettingFilePath(), JSONSettings::GetFormat(), QCoreApplication::instance());
    PaperControl_Worker::SerialConfig param;

    param.port = settings.value(JSON_PAPERCONTROL_DEV).toString();
    param.baud = settings.value(JSON_PAPERCONTROL_BAUD).toUInt();
    param.data_bits = (QSerialPort::DataBits) settings.value(JSON_PAPERCONTROL_DATABITS).toUInt();
    param.stop_bits = (QSerialPort::StopBits) settings.value(JSON_PAPERCONTROL_STOPBITS).toUInt();
    param.parity = (QSerialPort::Parity) settings.value(JSON_PAPERCONTROL_PARITY).toUInt();

	m_coordinator = qobject_cast<Coordinator *>(parent);

    m_worker = new PaperControl_Worker();
	m_worker->setSerialParam(param);
	m_worker->moveToThread(&m_worker_thread);

	connect(&m_worker_thread, &QThread::finished, m_worker, &QObject::deleteLater);
    connect(this, &PaperController::start, m_worker, &PaperControl_Worker::start_work);
    connect(this, &PaperController::stop, m_worker, &PaperControl_Worker::stop_work);

    m_worker_thread.setObjectName("ControlBoard Thread");
	m_worker_thread.start();
}

PaperController::~PaperController()
{
	m_worker_thread.quit();
	m_worker_thread.wait();
}

